#include <basic/errcode.h>
#include <basic/datetime.h>
#include <basic/sockd.h>

enum _wait_option
{/*{{{*/
    _RECV_=0,
    _SEND_,
};/*}}}*/

static int _wait(unsigned int type, SOCKET sock, int *p_timeout_ms)
{/*{{{*/
    struct pollfd fd;
    int poll_num;
    struct timeval tv;
    long long begin;
    int used;

    if(!p_timeout_ms || *p_timeout_ms < 0)
        return(0);

    fd.fd=sock;
    fd.events=fd.revents=0;
    switch(type)
    {
        case _RECV_:
            fd.events|=POLLIN; break;
        case _SEND_:
            fd.events|=POLLOUT; break;
    }

    gettimeofday(&tv, NULL);
    begin=tv.tv_sec*1000LL+tv.tv_usec/1000;

POLL_AGAIN:
    poll_num=poll(&fd, 1, *p_timeout_ms);
    if(poll_num == SOCKET_ERROR)
    {
        if(get_last_error(NULL) == EINTR)
            goto POLL_AGAIN;
        return(-1);
    }
    // 超时
    if(poll_num == 0)
        return(RET_ERR_TIMEOUT);

    gettimeofday(&tv, NULL);
    used=tv.tv_sec*1000LL+tv.tv_usec/1000 - begin;
    *p_timeout_ms=*p_timeout_ms > used ? *p_timeout_ms - used : 0;

    return(0);
}/*}}}*/

SOCKET sockd_create()
{/*{{{*/
    return(socket(AF_INET, SOCK_DGRAM, 0));
}/*}}}*/

SOCKET sockd_addr_convert(char *saddr, struct sockd_addr *addr)
{/*{{{*/
    struct host_addr_info hainfo;
    char ip[IP_SIZE+1];

    if(!saddr || !addr)
        return(RET_ERR_PARA);

    if(host_addr_parse(saddr, &hainfo) || hainfo.port == 0)
    {   return(-1);   }
    if(!host_name_to_ip(hainfo.host, ip))
    {   return(-2);   }
    if(inet_pton(AF_INET, ip, &addr->ip) <= 0)
    {   return(-3);   }
    addr->port=htons(hainfo.port);

    return(0);
}/*}}}*/

int sockd_recvfrom(SOCKET sock, struct sockd_addr *peer_addr,
        unsigned char *msg, int *msg_size, int *p_timeout_ms)
{/*{{{*/
    struct sockaddr_in peer;
    socklen_t peer_size;
    int nread;
    int res;

    if(sock < 0 || !msg || !msg_size)
        return(RET_ERR_PARA);
    if(peer_addr)
    {
        peer_addr->ip=0;
        peer_addr->port=0;
    }
    CLR_BUF(msg); *msg_size=0;

    if((res=_wait(_RECV_, sock, p_timeout_ms)))
        return(res);
    peer_size=sizeof(peer);
    if((nread=recvfrom(sock, msg, SOCKD_MSG_SIZE, 0,
                    (struct sockaddr *)&peer, &peer_size)) ==
            SOCKET_ERROR)
        return(-2);
    msg[nread]=ZERO;
    *msg_size=nread;
    if(peer_addr)
    {
        peer_addr->ip=peer.sin_addr.s_addr;
        peer_addr->port=peer.sin_port;
    }

    return(0);
}/*}}}*/

int sockd_sendto(SOCKET sock, struct sockd_addr *peer_addr,
        unsigned char *msg, int *msg_size, int *p_timeout_ms)
{/*{{{*/
    struct sockaddr_in peer;
    int msg_out_size;
    int nwrite;
    int res;

    if(sock < 0 || !peer_addr || !msg || !msg_size)
        return(RET_ERR_PARA);
    if((msg_out_size=*msg_size) < 0 || msg_out_size > SOCKD_MSG_SIZE)
        return(RET_ERR_PARA);

    if((res=_wait(_SEND_, sock, p_timeout_ms)))
        return(res);
    peer.sin_family=AF_INET;
    peer.sin_addr.s_addr=peer_addr->ip;
    peer.sin_port=peer_addr->port;
    if((nwrite=sendto(sock, msg, msg_out_size, 0,
                    (const struct sockaddr *)&peer, sizeof(peer))) ==
            SOCKET_ERROR)
        return(-2);

    return(0); 
}/*}}}*/

SOCKET sockd_svr_listen(int server_port)
{/*{{{*/
    SOCKET sock;
    struct sockaddr_in server;

    if((sock=sockd_create()) == INVALID_SOCKET)
		return(INVALID_SOCKET);
    memset(&server, 0, sizeof(server));
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=htonl(INADDR_ANY);
    server.sin_port=htons(server_port);
    if(bind(sock, (const struct sockaddr *)&server, sizeof(server)) ==
            SOCKET_ERROR)
        return(INVALID_SOCKET);

    return(sock);
}/*}}}*/

SOCKET sockd_cli_connect(char *peer_addr)
{/*{{{*/
    struct sockd_addr addr;
    SOCKET sock;
    struct sockaddr_in peer;

    if(!peer_addr || sockd_addr_convert(peer_addr, &addr))
        return(INVALID_SOCKET);
    if((sock=sockd_create()) == INVALID_SOCKET)
        return(INVALID_SOCKET);
    memset(&peer, 0, sizeof(peer));
    peer.sin_family=AF_INET;
    peer.sin_port=addr.port;
    peer.sin_addr.s_addr=addr.ip;
    if(connect(sock, (const struct sockaddr *)&peer,
                    sizeof(peer)) == SOCKET_ERROR)
    {
        close(sock);
        return(INVALID_SOCKET);
    }

    return(sock);
}/*}}}*/

int sockd_cli_recv(SOCKET sock,
        unsigned char *msg, int *msg_size, int *p_timeout_ms)
{/*{{{*/
    int nread;
    int res;

    if(sock < 0 || !msg || !msg_size)
        return(RET_ERR_PARA);
    CLR_BUF(msg); *msg_size=0;

    if((res=_wait(_RECV_, sock, p_timeout_ms)))
        return(res);
    if((nread=recv(sock, msg, SOCKD_MSG_SIZE, 0)) == SOCKET_ERROR)
        return(-2);
    msg[nread]=ZERO;
    *msg_size=nread;

    return(0);
}/*}}}*/

int sockd_cli_send(SOCKET sock,
        unsigned char *msg, int *msg_size, int *p_timeout_ms)
{/*{{{*/
    int msg_out_size;
    int nwrite;
    int res;

    if(sock < 0 || !msg || !msg_size)
        return(RET_ERR_PARA);
    if((msg_out_size=*msg_size) < 0 || msg_out_size > SOCKD_MSG_SIZE)
        return(RET_ERR_PARA);

    if((res=_wait(_SEND_, sock, p_timeout_ms)))
        return(res);
    if((nwrite=send(sock, msg, msg_out_size, 0)) == SOCKET_ERROR)
        return(-2);

    return(0);
}/*}}}*/
